package com.kexio.pay.provider;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kexio.pay.config.WxPayProperties;
import com.kexio.pay.dto.PaymentRequest;
import com.kexio.pay.dto.PaymentResponse;
import com.kexio.pay.entity.PaymentCallback;
import com.kexio.pay.enums.PaymentMethod;
import com.kexio.pay.enums.PaymentStatus;
import com.kexio.pay.enums.SignType;
import com.kexio.pay.util.WxPayKit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

/**
 * 微信支付提供者（参考IJPay实现）
 * 
 * @author Kexio Team
 */
@Component
public class WechatPayProvider implements PaymentProvider {
    
    private static final Logger logger = LoggerFactory.getLogger(WechatPayProvider.class);
    private static final String PROVIDER_ID = "wechat";
    private static final String PROVIDER_NAME = "微信支付";
    
    // 微信支付API地址
    private static final String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    private static final String ORDER_QUERY_URL = "https://api.mch.weixin.qq.com/pay/orderquery";
    private static final String CLOSE_ORDER_URL = "https://api.mch.weixin.qq.com/pay/closeorder";
    
    @Autowired
    private WxPayProperties wxPayProperties;
    
    @Autowired
    private ObjectMapper objectMapper;
    
    @Override
    public String getProviderId() {
        return PROVIDER_ID;
    }
    
    @Override
    public String getProviderName() {
        return PROVIDER_NAME;
    }
    
    @Override
    public Set<PaymentMethod> getSupportedMethods() {
        Set<PaymentMethod> methods = new HashSet<>();
        methods.add(PaymentMethod.WECHAT_NATIVE);  // 扫码支付
        methods.add(PaymentMethod.WECHAT_JSAPI);   // 公众号支付
        methods.add(PaymentMethod.WECHAT_H5);      // H5支付
        methods.add(PaymentMethod.WECHAT_APP);     // APP支付
        methods.add(PaymentMethod.WECHAT_MINI);    // 小程序支付
        return methods;
    }
    
    @Override
    public boolean isSupported(PaymentMethod method) {
        return getSupportedMethods().contains(method);
    }
    
    @Override
    public PaymentResponse createPayment(PaymentRequest request) {
        try {
            PaymentMethod method = PaymentMethod.fromCode(request.getPaymentMethod());
            logger.info("创建微信支付订单: orderNo={}, amount={}, method={}", 
                       request.getOrderNo(), request.getAmount(), method.getDescription());
            
            // 构建统一下单参数（参考IJPay）
            Map<String, String> params = new TreeMap<>();
            params.put("appid", wxPayProperties.getAppId());
            params.put("mch_id", wxPayProperties.getMchId());
            params.put("nonce_str", WxPayKit.generateStr());
            params.put("body", request.getSubject());
            params.put("out_trade_no", request.getOrderNo());
            
            // 金额转换：元 -> 分
            int totalFee = request.getAmount()
                .multiply(BigDecimal.valueOf(100))
                .setScale(0, RoundingMode.HALF_UP)
                .intValue();
            params.put("total_fee", String.valueOf(totalFee));
            
            params.put("spbill_create_ip", request.getClientIp() != null ? request.getClientIp() : "127.0.0.1");
            params.put("notify_url", wxPayProperties.getDomain() + "/api/forum/payment/wechat/notify");
            
            // 根据支付方式设置trade_type和其他参数
            setTradeTypeAndParams(method, params, request);
            
            if (StringUtils.hasText(request.getBody())) {
                params.put("attach", request.getBody());
            }
            
            // 生成签名（参考IJPay）
            String sign = WxPayKit.createSign(params, wxPayProperties.getApiKey(), SignType.MD5);
            params.put("sign", sign);
            
            // 转换为XML
            String xmlData = WxPayKit.mapToXml(params);
            
            logger.debug("微信统一下单请求: {}", xmlData);
            
            // 发送请求
            HttpResponse response = HttpRequest.post(UNIFIED_ORDER_URL)
                .body(xmlData)
                .timeout(10000)
                .execute();
            
            String xmlResult = response.body();
            logger.debug("微信统一下单响应: {}", xmlResult);
            
            // 解析响应
            Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
            
            // 构建响应
            PaymentResponse paymentResponse = new PaymentResponse();
            paymentResponse.setOrderNo(request.getOrderNo());
            
            String returnCode = result.get("return_code");
            String resultCode = result.get("result_code");
            
            if (WxPayKit.isSuccess(returnCode) && WxPayKit.isSuccess(resultCode)) {
                String codeUrl = result.get("code_url");
                String prepayId = result.get("prepay_id");
                
                if (StringUtils.hasText(codeUrl)) {
                    logger.info("微信支付订单创建成功: orderNo={}, codeUrl={}", 
                               request.getOrderNo(), codeUrl);
                    
                    paymentResponse.setSuccess(true);
                    paymentResponse.setPaymentUrl(codeUrl);
                    paymentResponse.setThirdPartyOrderNo(prepayId);
                    paymentResponse.setMessage("订单创建成功");
                } else {
                    logger.warn("微信支付返回成功但未获取到code_url: orderNo={}", request.getOrderNo());
                    paymentResponse.setSuccess(false);
                    paymentResponse.setMessage("未获取到支付二维码");
                }
            } else {
                String errorMsg = result.get("return_msg");
                if (!WxPayKit.isSuccess(resultCode)) {
                    errorMsg = result.get("err_code_des");
                }
                
                logger.error("微信支付订单创建失败: orderNo={}, returnCode={}, resultCode={}, error={}", 
                            request.getOrderNo(), returnCode, resultCode, errorMsg);
                
                paymentResponse.setSuccess(false);
                paymentResponse.setErrorCode(result.get("err_code"));
                paymentResponse.setMessage("订单创建失败: " + errorMsg);
            }
            
            return paymentResponse;
            
        } catch (Exception e) {
            logger.error("调用微信支付API异常: orderNo={}", request.getOrderNo(), e);
            
            PaymentResponse paymentResponse = new PaymentResponse();
            paymentResponse.setSuccess(false);
            paymentResponse.setOrderNo(request.getOrderNo());
            paymentResponse.setMessage("API调用失败: " + e.getMessage());
            
            return paymentResponse;
        }
    }
    
    @Override
    public PaymentStatus queryPayment(String orderNo, String thirdPartyOrderNo) {
        try {
            logger.info("查询微信支付订单状态: orderNo={}", orderNo);
            
            Map<String, String> params = new TreeMap<>();
            params.put("appid", wxPayProperties.getAppId());
            params.put("mch_id", wxPayProperties.getMchId());
            params.put("out_trade_no", orderNo);
            params.put("nonce_str", WxPayKit.generateStr());
            
            // 生成签名
            String sign = WxPayKit.createSign(params, wxPayProperties.getApiKey(), SignType.MD5);
            params.put("sign", sign);
            
            // 转换为XML
            String xmlData = WxPayKit.mapToXml(params);
            
            // 发送请求
            HttpResponse response = HttpRequest.post(ORDER_QUERY_URL)
                .body(xmlData)
                .timeout(10000)
                .execute();
            
            String xmlResult = response.body();
            Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
            
            if (WxPayKit.isSuccess(result)) {
                String tradeState = result.get("trade_state");
                logger.info("微信订单状态查询成功: orderNo={}, tradeState={}", orderNo, tradeState);
                
                // 转换交易状态
                switch (tradeState) {
                    case "SUCCESS":
                        return PaymentStatus.SUCCESS;
                    case "REFUND":
                        return PaymentStatus.REFUNDED;
                    case "NOTPAY":
                        return PaymentStatus.PENDING;
                    case "CLOSED":
                        return PaymentStatus.CLOSED;
                    case "REVOKED":
                        return PaymentStatus.CANCELLED;
                    case "USERPAYING":
                        return PaymentStatus.PENDING;
                    case "PAYERROR":
                    default:
                        return PaymentStatus.FAILED;
                }
            } else {
                logger.warn("微信订单状态查询失败: orderNo={}, error={}", 
                           orderNo, result.get("return_msg"));
                return PaymentStatus.FAILED;
            }
            
        } catch (Exception e) {
            logger.error("查询微信订单异常: orderNo={}", orderNo, e);
            return PaymentStatus.FAILED;
        }
    }
    
    @Override
    public boolean closePayment(String orderNo, String thirdPartyOrderNo) {
        try {
            logger.info("关闭微信支付订单: orderNo={}", orderNo);
            
            Map<String, String> params = new TreeMap<>();
            params.put("appid", wxPayProperties.getAppId());
            params.put("mch_id", wxPayProperties.getMchId());
            params.put("out_trade_no", orderNo);
            params.put("nonce_str", WxPayKit.generateStr());
            
            // 生成签名
            String sign = WxPayKit.createSign(params, wxPayProperties.getApiKey(), SignType.MD5);
            params.put("sign", sign);
            
            // 转换为XML
            String xmlData = WxPayKit.mapToXml(params);
            
            // 发送请求
            HttpResponse response = HttpRequest.post(CLOSE_ORDER_URL)
                .body(xmlData)
                .timeout(10000)
                .execute();
            
            String xmlResult = response.body();
            Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
            
            if (WxPayKit.isSuccess(result)) {
                logger.info("微信订单关闭成功: orderNo={}", orderNo);
                return true;
            } else {
                logger.warn("微信订单关闭失败: orderNo={}, error={}", 
                           orderNo, result.get("return_msg"));
                return false;
            }
            
        } catch (Exception e) {
            logger.error("关闭微信订单异常: orderNo={}", orderNo, e);
            return false;
        }
    }
    
    @Override
    public boolean verifyCallback(PaymentCallback callback) {
        if (callback == null || callback.getCallbackParams() == null) {
            logger.warn("回调数据为空，无法验证签名");
            return false;
        }
        
        try {
            // 将Map<String, String[]>转换为Map<String, String>
            Map<String, String> params = new HashMap<>();
            callback.getCallbackParams().forEach((key, value) -> {
                if (value != null && value.length > 0) {
                    params.put(key, value[0]);
                }
            });
            
            // 验证签名
            boolean signVerified = WxPayKit.verifySign(params, wxPayProperties.getApiKey(), SignType.MD5);
            
            if (!signVerified) {
                logger.warn("微信回调签名验证失败: orderNo={}", callback.getOrderNo());
            }
            return signVerified;
        } catch (Exception e) {
            logger.error("微信回调签名验证异常: orderNo={}", callback.getOrderNo(), e);
            return false;
        }
    }
    
    @Override
    public CallbackResult processCallback(PaymentCallback callback) {
        if (!verifyCallback(callback)) {
            return CallbackResult.failure("签名验证失败");
        }
        
        Map<String, String[]> params = callback.getCallbackParams();
        String returnCode = params.get("return_code") != null ? params.get("return_code")[0] : null;
        String resultCode = params.get("result_code") != null ? params.get("result_code")[0] : null;
        
        if (!"SUCCESS".equals(returnCode) || !"SUCCESS".equals(resultCode)) {
            return CallbackResult.failure("交易失败");
        }
        
        String outTradeNo = params.get("out_trade_no") != null ? params.get("out_trade_no")[0] : null;
        String transactionId = params.get("transaction_id") != null ? params.get("transaction_id")[0] : null;
        String totalFeeStr = params.get("total_fee") != null ? params.get("total_fee")[0] : null;
        
        if (!StringUtils.hasText(totalFeeStr)) {
            return CallbackResult.failure("金额参数缺失");
        }
        
        try {
            // 分 -> 元
            BigDecimal actualAmount = new BigDecimal(totalFeeStr)
                .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
            
            // 支付成功
            return CallbackResult.success(outTradeNo, PaymentStatus.SUCCESS);
        } catch (NumberFormatException e) {
            logger.error("解析金额失败: totalFee={}", totalFeeStr, e);
            return CallbackResult.failure("金额格式错误");
        }
    }
    
    @Override
    public boolean isConfigured() {
        return StringUtils.hasText(wxPayProperties.getAppId())
            && StringUtils.hasText(wxPayProperties.getMchId())
            && StringUtils.hasText(wxPayProperties.getApiKey());
    }
    
    @Override
    public Map<String, String> getConfigInfo() {
        Map<String, String> info = new LinkedHashMap<>();
        info.put("providerId", PROVIDER_ID);
        info.put("providerName", PROVIDER_NAME);
        info.put("appId", wxPayProperties.getAppId());
        info.put("mchId", wxPayProperties.getMchId());
        info.put("apiVersion", wxPayProperties.getApiVersion());
        info.put("configured", String.valueOf(isConfigured()));
        return info;
    }
    
    /**
     * 根据支付方式设置trade_type和其他参数
     */
    private void setTradeTypeAndParams(PaymentMethod method, Map<String, String> params, PaymentRequest request) {
        switch (method) {
            case WECHAT_NATIVE:
                // Native扫码支付
                params.put("trade_type", "NATIVE");
                break;
                
            case WECHAT_JSAPI:
                // 公众号支付
                params.put("trade_type", "JSAPI");
                // JSAPI需要openid
                if (StringUtils.hasText(request.getExtraData())) {
                    // 从extraData中获取openid
                    params.put("openid", request.getExtraData());
                } else {
                    logger.warn("JSAPI支付缺少openid参数: orderNo={}", request.getOrderNo());
                }
                break;
                
            case WECHAT_H5:
                // H5支付
                params.put("trade_type", "MWEB");
                // H5支付需要场景信息
                if (StringUtils.hasText(request.getClientIp())) {
                    // 构建scene_info
                    String sceneInfo = buildH5SceneInfo(request);
                    params.put("scene_info", sceneInfo);
                }
                break;
                
            case WECHAT_APP:
                // APP支付
                params.put("trade_type", "APP");
                break;
                
            case WECHAT_MINI:
                // 小程序支付
                params.put("trade_type", "JSAPI");
                // 小程序需要openid
                if (StringUtils.hasText(request.getExtraData())) {
                    params.put("openid", request.getExtraData());
                } else {
                    logger.warn("小程序支付缺少openid参数: orderNo={}", request.getOrderNo());
                }
                break;
                
            default:
                logger.warn("未知的支付方式: {}", method);
                params.put("trade_type", "NATIVE"); // 默认使用NATIVE
        }
    }
    
    /**
     * 构建H5支付场景信息
     */
    private String buildH5SceneInfo(PaymentRequest request) {
        // 简化版本，实际应该从request中获取更详细的信息
        return "{\"h5_info\":{\"type\":\"Wap\",\"wap_url\":\"" + 
               wxPayProperties.getDomain() + 
               "\",\"wap_name\":\"Kexio论坛\"}}";
    }
}
